/*
 * Copyright 2015-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.statemachine.config;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.SmartLifecycle;
import org.springframework.messaging.Message;
import org.springframework.statemachine.ExtendedState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.action.Actions;
import org.springframework.statemachine.config.model.*;
import org.springframework.statemachine.config.model.verifier.CompositeStateMachineModelVerifier;
import org.springframework.statemachine.config.model.verifier.StateMachineModelVerifier;
import org.springframework.statemachine.ensemble.DistributedStateMachine;
import org.springframework.statemachine.listener.StateMachineListener;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.monitor.StateMachineMonitor;
import org.springframework.statemachine.region.Region;
import org.springframework.statemachine.security.StateMachineSecurityInterceptor;
import org.springframework.statemachine.state.*;
import org.springframework.statemachine.state.ChoicePseudoState.ChoiceStateData;
import org.springframework.statemachine.state.JoinPseudoState.JoinStateData;
import org.springframework.statemachine.state.JunctionPseudoState.JunctionStateData;
import org.springframework.statemachine.support.DefaultExtendedState;
import org.springframework.statemachine.support.LifecycleObjectSupport;
import org.springframework.statemachine.support.StateMachineInterceptor;
import org.springframework.statemachine.support.StateMachineInterceptorAdapter;
import org.springframework.statemachine.support.tree.Tree;
import org.springframework.statemachine.support.tree.Tree.Node;
import org.springframework.statemachine.support.tree.TreeTraverser;
import org.springframework.statemachine.transition.*;
import org.springframework.statemachine.trigger.EventTrigger;
import org.springframework.statemachine.trigger.TimerTrigger;
import org.springframework.statemachine.trigger.Trigger;
import org.springframework.util.ObjectUtils;
import reactor.core.publisher.Mono;

import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * 重写jar里的类 ： 完全相同的路径
 * 修改了 buildMachine 方法
 * @param <S>
 * @param <E>
 */
public abstract class AbstractStateMachineFactory<S, E> extends LifecycleObjectSupport implements
        StateMachineFactory<S, E>, BeanNameAware {

    private final Log log = LogFactory.getLog(AbstractStateMachineFactory.class);

    private final StateMachineModel<S, E> defaultStateMachineModel;

    private final StateMachineModelFactory<S, E> stateMachineModelFactory;

    private Boolean contextEvents;

    private boolean handleAutostartup = false;

    private String beanName;

    private StateMachineMonitor<S, E> defaultStateMachineMonitor;

    /**
     * Instantiates a new abstract state machine factory.
     *
     * @param defaultStateMachineModel the default state machine model
     * @param stateMachineModelFactory the state machine model factory
     */
    public AbstractStateMachineFactory(StateMachineModel<S, E> defaultStateMachineModel, StateMachineModelFactory<S, E> stateMachineModelFactory) {
        this.stateMachineModelFactory = stateMachineModelFactory;
        this.defaultStateMachineModel = defaultStateMachineModel;
    }

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
    }

    @Override
    public StateMachine<S, E> getStateMachine() {
        return getStateMachine(null, null);
    }

    @Override
    public StateMachine<S, E> getStateMachine(String machineId) {
        return getStateMachine(null, machineId);
    }

    @Override
    public StateMachine<S, E> getStateMachine(UUID uuid) {
        return getStateMachine(uuid, null);
    }

    /**
     * Main constructor that create a {@link StateMachine}.
     *
     * @param uuid for internal usage. Can be null, in that case a random one will be generated.
     * @param machineId represent a user Id, up to you to set what you want.
     * @return a {@link StateMachine}
     */
    @SuppressWarnings("unchecked")
    public StateMachine<S, E> getStateMachine(UUID uuid, String machineId) {
        ArrayList<StateMachine<S, E>> machines = new ArrayList<>();

        StateMachineModel<S, E> stateMachineModel = resolveStateMachineModel(machineId);
        if (stateMachineModel.getConfigurationData().isVerifierEnabled()) {
            StateMachineModelVerifier<S, E> verifier = stateMachineModel.getConfigurationData().getVerifier();
            if (verifier == null) {
                verifier = new CompositeStateMachineModelVerifier<S, E>();
            }
            verifier.verify(stateMachineModel);
        }

        // shared
        DefaultExtendedState defaultExtendedState = new DefaultExtendedState();

        StateMachine<S, E> machine = null;

        // we store mappings from state id's to states which gets
        // created during the process. This is needed for transitions to
        // find a correct mappings because they use state id's, not actual
        // states.
        final Map<S, State<S, E>> stateMap = new HashMap<S, State<S, E>>();
        Stack<MachineStackItem<S, E>> regionStack = new Stack<MachineStackItem<S, E>>();
        Stack<StateData<S, E>> stateStack = new Stack<StateData<S, E>>();
        Map<Object, StateMachine<S, E>> machineMap = new HashMap<Object, StateMachine<S,E>>();
        List<HolderListItem<S, E>> holderList = new ArrayList<>();

        Iterator<Node<StateData<S, E>>> iterator = buildStateDataIterator(stateMachineModel);
        while (iterator.hasNext()) {
            Node<StateData<S, E>> node = iterator.next();
            StateData<S, E> stateData = node.getData();
            StateData<S, E> peek = stateStack.isEmpty() ? null : stateStack.peek();

            // simply push and continue
            if (stateStack.isEmpty()) {
                stateStack.push(stateData);
                continue;
            }

            boolean stackContainsSameParent = false;
            Iterator<StateData<S, E>> ii = stateStack.iterator();
            while (ii.hasNext()) {
                StateData<S, E> sd = ii.next();
                if (stateData != null && ObjectUtils.nullSafeEquals(stateData.getState(), sd.getParent())) {
                    stackContainsSameParent = true;
                    break;
                }
            }

            if (stateData != null && !stackContainsSameParent) {
                stateStack.push(stateData);
                continue;
            }

            Collection<StateData<S, E>> stateDatas = popSameParents(stateStack);
            int initialCount = getInitialCount(stateDatas);
            Collection<Collection<StateData<S, E>>> regionsStateDatas = splitIntoRegions(stateDatas);
            Collection<TransitionData<S, E>> transitionsData = getTransitionData(iterator.hasNext(), stateDatas, stateMachineModel);

            if (initialCount > 1) {
                for (Collection<StateData<S, E>> regionStateDatas : regionsStateDatas) {
                    // try to build reqion id's
                    Object rId = regionStateDatas.iterator().next().getRegion();
                    String mId = machineId != null ? machineId : stateMachineModel.getConfigurationData().getMachineId();
                    mId = mId + "#" + (rId != null ? rId.toString() : "");

                    machine = buildMachine(machineMap, stateMap, holderList, regionStateDatas, transitionsData,
                            resolveBeanFactory(stateMachineModel), contextEvents, defaultExtendedState,
                            stateMachineModel.getTransitionsData(), mId, null, stateMachineModel);
                    regionStack.push(new MachineStackItem<S, E>(machine));
                    machines.add(machine);
                }

                Collection<Region<S, E>> regions = new ArrayList<Region<S, E>>();
                while (!regionStack.isEmpty()) {
                    MachineStackItem<S, E> pop = regionStack.pop();
                    regions.add(pop.machine);
                }
                S parent = (S)peek.getParent();
                RegionState<S, E> rstate = buildRegionStateInternal(parent, regions, null,
                        stateData != null ? stateData.getEntryActions() : null,
                        stateData != null ? stateData.getExitActions() : null,
                        new DefaultPseudoState<S, E>(PseudoStateKind.INITIAL), stateMachineModel);
                rstate.setRegionExecutionPolicy(stateMachineModel.getConfigurationData().getRegionExecutionPolicy());
                if (stateData != null) {
                    stateMap.put(stateData.getState(), rstate);
                } else {
                    // TODO: don't like that we create a last machine here
                    Collection<State<S, E>> states = new ArrayList<State<S, E>>();
                    states.add(rstate);
                    Transition<S, E> initialTransition = new InitialTransition<S, E>(rstate);
                    StateMachine<S, E> m = buildStateMachineInternal(states, new ArrayList<Transition<S, E>>(), rstate, initialTransition,
                            null, defaultExtendedState, null, contextEvents, resolveBeanFactory(stateMachineModel), beanName,
                            machineId != null ? machineId : stateMachineModel.getConfigurationData().getMachineId(),
                            uuid, stateMachineModel);
                    machine = m;
                    machines.add(m);
                }
            } else {
                machine = buildMachine(machineMap, stateMap, holderList, stateDatas, transitionsData,
                        resolveBeanFactory(stateMachineModel), contextEvents, defaultExtendedState,
                        stateMachineModel.getTransitionsData(), machineId, uuid, stateMachineModel);
                machines.add(machine);
                if (peek.isInitial() || (!peek.isInitial() && !machineMap.containsKey(peek.getParent()))) {
                    machineMap.put(peek.getParent(), machine);
                }
            }

            stateStack.push(stateData);
        }

        // setup autostart for top-level machine
        if (machine instanceof LifecycleObjectSupport) {
            ((LifecycleObjectSupport)machine).setAutoStartup(stateMachineModel.getConfigurationData().isAutoStart());
        }

        // set top-level machine as relay
        final StateMachine<S, E> fmachine = machine;
        fmachine.getStateMachineAccessor().doWithAllRegions(function -> function.setRelay(fmachine));

        // add monitoring hooks
        final StateMachineMonitor<S, E> stateMachineMonitor = stateMachineModel.getConfigurationData().getStateMachineMonitor();
        if (stateMachineMonitor != null || defaultStateMachineMonitor != null) {
            fmachine.getStateMachineAccessor().doWithRegion(function -> {
                if (defaultStateMachineMonitor != null) {
                    function.addStateMachineMonitor(defaultStateMachineMonitor);
                }
                if (stateMachineMonitor != null) {
                    function.addStateMachineMonitor(stateMachineMonitor);
                }
            });
        }

        // set parent machines for each built machine
        for (Entry<Object, StateMachine<S, E>> mme : machineMap.entrySet()) {
            StateMachine<S, E> m = null;
            if (mme.getKey() != null) {
                Object sParent = null;
                for (StateData<S, E> sd : stateMachineModel.getStatesData().getStateData()) {
                    if (ObjectUtils.nullSafeEquals(sd.getState(), mme.getKey())) {
                        sParent = sd.getParent();
                        break;
                    }
                }
                m = machineMap.get(sParent);
            }
            final StateMachine<S, E> mm = m;
            mme.getValue().getStateMachineAccessor().doWithRegion(function -> function.setParentMachine(mm));
        }

        // init built machines
        for (StateMachine<S, E> m : machines) {
            ((LifecycleObjectSupport)m).afterPropertiesSet();
        }


        // TODO: should error out if sec is enabled but spring-security is not in cp
        if (stateMachineModel.getConfigurationData().isSecurityEnabled()) {
            final StateMachineSecurityInterceptor<S, E> securityInterceptor = new StateMachineSecurityInterceptor<S, E>(
                    stateMachineModel.getConfigurationData().getTransitionSecurityAccessDecisionManager(),
                    stateMachineModel.getConfigurationData().getEventSecurityAccessDecisionManager(),
                    stateMachineModel.getConfigurationData().getEventSecurityRule());
            log.info("Adding security interceptor " + securityInterceptor);
            fmachine.getStateMachineAccessor()
                    .doWithAllRegions(function -> function.addStateMachineInterceptor(securityInterceptor));
        }

        // setup distributed state machine if needed.
        // we wrap previously build machine with a distributed
        // state machine and set it to use given ensemble.
        if (stateMachineModel.getConfigurationData().getStateMachineEnsemble() != null) {
            DistributedStateMachine<S, E> distributedStateMachine = new DistributedStateMachine<S, E>(
                    stateMachineModel.getConfigurationData().getStateMachineEnsemble(), machine);
            distributedStateMachine.setAutoStartup(stateMachineModel.getConfigurationData().isAutoStart());
            distributedStateMachine.afterPropertiesSet();
            machine = distributedStateMachine;
        }

        for (StateMachineListener<S, E> listener : stateMachineModel.getConfigurationData().getStateMachineListeners()) {
            machine.addStateListener(listener);
        }

        List<StateMachineInterceptor<S,E>> interceptors = stateMachineModel.getConfigurationData().getStateMachineInterceptors();
        if (interceptors != null) {

            for (final StateMachineInterceptor<S, E> interceptor : interceptors) {
                // add persisting interceptor hooks to all regions
                RegionPersistingInterceptorAdapter<S, E> adapter = new RegionPersistingInterceptorAdapter<>(interceptor);
                machine.getStateMachineAccessor().doWithAllRegions(function -> function.addStateMachineInterceptor(adapter));
            }
        }

        // go through holders and fix state references which
        // were not known at a time holder was created
        for (HolderListItem<S, E> holderItem : holderList) {
            holderItem.value.setState(stateMap.get(holderItem.key));
        }

        return delegateAutoStartup(machine);
    }

    private static class RegionPersistingInterceptorAdapter<S, E> extends StateMachineInterceptorAdapter<S, E> {

        private final StateMachineInterceptor<S, E> interceptor;

        public RegionPersistingInterceptorAdapter(StateMachineInterceptor<S, E> interceptor) {
            this.interceptor = interceptor;
        }

        @Override
        public Message<E> preEvent(Message<E> message, StateMachine<S, E> stateMachine) {
            return interceptor.preEvent(message, stateMachine);
        }

        @Override
        public StateContext<S, E> preTransition(StateContext<S, E> stateContext) {
            return interceptor.preTransition(stateContext);
        }

        @Override
        public StateContext<S, E> postTransition(StateContext<S, E> stateContext) {
            return interceptor.postTransition(stateContext);
        }

        @Override
        public Exception stateMachineError(StateMachine<S, E> stateMachine, Exception exception) {
            return interceptor.stateMachineError(stateMachine, exception);
        }

        @Override
        public void preStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
                                   StateMachine<S, E> stateMachine, StateMachine<S, E> rootStateMachine) {
            interceptor.preStateChange(state, message, transition, stateMachine, rootStateMachine);
        }

        @Override
        public void postStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
                                    StateMachine<S, E> stateMachine, StateMachine<S, E> rootStateMachine) {
            interceptor.postStateChange(state, message, transition, stateMachine, rootStateMachine);
        }

    }

    /**
     * Instructs this factory to handle auto-start flag manually
     * by calling lifecycle start method.
     *
     * @param handleAutostartup the new handle autostartup
     */
    public void setHandleAutostartup(boolean handleAutostartup) {
        this.handleAutostartup = handleAutostartup;
    }

    /**
     * Instructs this factory to enable application context events.
     *
     * @param contextEvents the new context events enabled
     */
    public void setContextEventsEnabled(Boolean contextEvents) {
        this.contextEvents = contextEvents;
    }

    /**
     * Set state machine monitor.
     *
     * @param stateMachineMonitor the state machine monitor
     */
    public void setStateMachineMonitor(StateMachineMonitor<S, E> stateMachineMonitor) {
        this.defaultStateMachineMonitor = stateMachineMonitor;
    }

    private StateMachine<S, E> delegateAutoStartup(StateMachine<S, E> delegate) {
        if (handleAutostartup && delegate instanceof SmartLifecycle && ((SmartLifecycle) delegate).isAutoStartup()) {
            AutostartListener<S, E> autostartListener = new AutostartListener<>();
            delegate.addStateListener(autostartListener);
            ((SmartLifecycle)delegate).start();
            try {
                autostartListener.latch.await(30, TimeUnit.SECONDS);
            } catch (Exception e) {
                log.warn("Waited 30 seconds for machine to start as autostart was requested, machine may not be ready");
            } finally {
                delegate.removeStateListener(autostartListener);
            }
        }
        return delegate;
    }

    protected BeanFactory resolveBeanFactory(StateMachineModel<S, E> stateMachineModel) {
        if (stateMachineModel.getConfigurationData().getBeanFactory() != null) {
            return stateMachineModel.getConfigurationData().getBeanFactory();
        } else {
            return getBeanFactory();
        }
    }

    protected StateMachineModel<S, E> resolveStateMachineModel(String machineId) {
        if (stateMachineModelFactory == null) {
            return defaultStateMachineModel;
        } else {
            StateMachineModel<S, E> m = stateMachineModelFactory.build(machineId);
            if (m.getConfigurationData() == null) {
                // if model doesn't have explicit configuration data,
                // get it from default model
                return new DefaultStateMachineModel<>(defaultStateMachineModel.getConfigurationData(), m.getStatesData(),
                        m.getTransitionsData());
            } else {
                return m;
            }
        }
    }

    private int getInitialCount(Collection<StateData<S, E>> stateDatas) {
        int count = 0;
        for (StateData<S, E> stateData : stateDatas) {
            if (stateData.isInitial()) {
                count++;
            }
        }
        return count;
    }

    private Collection<Collection<StateData<S, E>>> splitIntoRegions(Collection<StateData<S, E>> stateDatas) {
        Map<Object, Collection<StateData<S, E>>> map = new HashMap<Object, Collection<StateData<S, E>>>();
        for (StateData<S, E> stateData : stateDatas) {
            Collection<StateData<S, E>> c = map.get(stateData.getRegion());
            if (c == null) {
                c = new ArrayList<StateData<S,E>>();
            }
            c.add(stateData);
            map.put(stateData.getRegion(), c);
        }
        return map.values();
    }

    private Collection<TransitionData<S, E>> getTransitionData(boolean roots, Collection<StateData<S, E>> stateDatas, StateMachineModel<S, E> stateMachineModel) {
        if (roots) {
            return resolveTransitionData(stateMachineModel.getTransitionsData().getTransitions(), stateDatas);
        } else {
            return resolveTransitionData2(stateMachineModel.getTransitionsData().getTransitions());
        }
    }

    private static <S, E> Collection<StateData<S, E>> popSameParents(Stack<StateData<S, E>> stack) {
        Collection<StateData<S, E>> data = new ArrayList<StateData<S, E>>();
        Object parent = null;
        if (!stack.isEmpty()) {
            parent = stack.peek().getParent();
        }
        while (!stack.isEmpty() && ObjectUtils.nullSafeEquals(parent, stack.peek().getParent())) {
            data.add(stack.pop());
        }
        return data;
    }


    private static class MachineStackItem<S, E> {

        StateMachine<S, E> machine;

        public MachineStackItem(StateMachine<S, E> machine) {
            this.machine = machine;
        }

    }

    private Collection<TransitionData<S, E>> resolveTransitionData(Collection<TransitionData<S, E>> in, Collection<StateData<S, E>> stateDatas) {
        ArrayList<TransitionData<S, E>> out = new ArrayList<TransitionData<S,E>>();

        Collection<Object> states = new ArrayList<Object>();
        for (StateData<S, E> stateData : stateDatas) {
            states.add(stateData.getParent());
        }

        for (TransitionData<S, E> transitionData : in) {
            S state = transitionData.getState();
            if (state != null && states.contains(state)) {
                out.add(transitionData);
            }
        }

        return out;
    }

    private Collection<TransitionData<S, E>> resolveTransitionData2(Collection<TransitionData<S, E>> in) {
        ArrayList<TransitionData<S, E>> out = new ArrayList<TransitionData<S,E>>();
        for (TransitionData<S, E> transitionData : in) {
            if (transitionData.getState() == null) {
                out.add(transitionData);
            }
        }
        return out;
    }


    @SuppressWarnings("unchecked")
    private StateMachine<S, E> buildMachine(Map<Object, StateMachine<S, E>> machineMap, Map<S, State<S, E>> stateMap,
                                            List<HolderListItem<S, E>> holderList, Collection<StateData<S, E>> stateDatas,
                                            Collection<TransitionData<S, E>> transitionsData, BeanFactory beanFactory, Boolean contextEvents,
                                            DefaultExtendedState defaultExtendedState, TransitionsData<S, E> stateMachineTransitions, String machineId,
                                            UUID uuid, StateMachineModel<S, E> stateMachineModel) {

        State<S, E> state = null;
        State<S, E> initialState = null;
        PseudoState<S, E> historyState = null;
        Action<S, E> initialAction = null;
        Collection<State<S, E>> states = new ArrayList<State<S,E>>();

        // for now loop twice and build states for
        // non initial/end pseudostates last

        for (StateData<S, E> stateData : stateDatas) {
            StateMachine<S, E> stateMachine = machineMap.get(stateData.getState());
            if (stateMachine == null) {
                // get a submachine from state data if we didn't have
                // it already. stays null if we don't have one.
                stateMachine = stateData.getSubmachine();
                if (stateMachine == null && stateData.getSubmachineFactory() != null) {
                    stateMachine = stateData.getSubmachineFactory().getStateMachine(machineId);
                }
            }
            state = stateMap.get(stateData.getState());
            if (state != null) {
                states.add(state);
                if (stateData.isInitial()) {
                    initialState = state;
                }
                continue;
            }
            if (stateMachine != null) {
                PseudoState<S, E> pseudoState = null;
                if (stateData.isInitial()) {
                    pseudoState = new DefaultPseudoState<S, E>(PseudoStateKind.INITIAL);
                }
                StateMachineState<S, E> stateMachineState = new StateMachineState<S, E>(stateData.getState(),
                        stateMachine, stateData.getDeferred(), stateData.getEntryActions(), stateData.getExitActions(),
                        pseudoState);
                stateMachineState
                        .setStateDoActionPolicy(stateMachineModel.getConfigurationData().getStateDoActionPolicy());
                stateMachineState.setStateDoActionPolicyTimeout(
                        stateMachineModel.getConfigurationData().getStateDoActionPolicyTimeout());
                state = stateMachineState;

                // TODO: below if/else doesn't feel right
                if (stateDatas.size() > 1 && stateData.isInitial()) {
                    initialState = state;
                    initialAction = stateData.getInitialAction();
                } else if (stateDatas.size() == 1) {
                    initialState = state;
                    initialAction = stateData.getInitialAction();
                }
                states.add(state);
            } else {
                PseudoState<S, E> pseudoState = null;
                if (stateData.isInitial()) {
                    pseudoState = new DefaultPseudoState<S, E>(PseudoStateKind.INITIAL);
                } else if (stateData.isEnd()) {
                    pseudoState = new DefaultPseudoState<S, E>(PseudoStateKind.END);
                } else if (stateData.getPseudoStateKind() == PseudoStateKind.HISTORY_SHALLOW) {
                    continue;
                } else if (stateData.getPseudoStateKind() == PseudoStateKind.HISTORY_DEEP) {
                    continue;
                } else if (stateData.getPseudoStateKind() == PseudoStateKind.JOIN) {
                    continue;
                } else if (stateData.getPseudoStateKind() == PseudoStateKind.FORK) {
                    continue;
                } else if (stateData.getPseudoStateKind() == PseudoStateKind.CHOICE) {
                    continue;
                } else if (stateData.getPseudoStateKind() == PseudoStateKind.JUNCTION) {
                    continue;
                } else if (stateData.getPseudoStateKind() == PseudoStateKind.ENTRY) {
                    continue;
                } else if (stateData.getPseudoStateKind() == PseudoStateKind.EXIT) {
                    continue;
                }
                state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(),
                        stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel);
                if (stateData.isInitial()) {
                    initialState = state;
                    initialAction = stateData.getInitialAction();
                }
                states.add(state);

            }
            stateMap.put(stateData.getState(), state);
        }

        for (StateData<S, E> stateData : stateDatas) {
            if (stateData.getPseudoStateKind() == PseudoStateKind.HISTORY_SHALLOW) {
                State<S, E> defaultState = null;
                S s = stateData.getState();
                Collection<HistoryData<S,E>> historys = stateMachineTransitions.getHistorys();
                if (historys == null) {
                    throw new MalformedConfigurationException("No transitions for state " + s);
                }
                for (HistoryData<S,E> history : historys) {
                    if (history.getSource().equals(s)) {
                        defaultState = stateMap.get(history.getTarget());
                    }
                }
                StateHolder<S, E> defaultStateHolder = new StateHolder<S, E>(defaultState);
                StateHolder<S, E> containingStateHolder = new StateHolder<S, E>(stateMap.get(stateData.getParent()));
                if (containingStateHolder.getState() == null) {
                    holderList.add(new HolderListItem<S, E>((S)stateData.getParent(), containingStateHolder));
                }
                PseudoState<S, E> pseudoState = new HistoryPseudoState<S, E>(PseudoStateKind.HISTORY_SHALLOW, defaultStateHolder, containingStateHolder);
                state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(),
                        stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel);
                states.add(state);
                stateMap.put(stateData.getState(), state);
                historyState = pseudoState;
            } else if (stateData.getPseudoStateKind() == PseudoStateKind.HISTORY_DEEP) {
                State<S, E> defaultState = null;
                S s = stateData.getState();
                Collection<HistoryData<S,E>> historys = stateMachineTransitions.getHistorys();
                if (historys == null) {
                    throw new MalformedConfigurationException("No transitions for state " + s);
                }
                for (HistoryData<S,E> history : historys) {
                    if (history.getSource().equals(s)) {
                        defaultState = stateMap.get(history.getTarget());
                    }
                }
                StateHolder<S, E> defaultStateHolder = new StateHolder<S, E>(defaultState);
                StateHolder<S, E> containingStateHolder = new StateHolder<S, E>(stateMap.get(stateData.getParent()));
                if (containingStateHolder.getState() == null) {
                    holderList.add(new HolderListItem<S, E>((S)stateData.getParent(), containingStateHolder));
                }
                PseudoState<S, E> pseudoState = new HistoryPseudoState<S, E>(PseudoStateKind.HISTORY_DEEP, defaultStateHolder, containingStateHolder);
                state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(),
                        stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel);
                states.add(state);
                stateMap.put(stateData.getState(), state);
                historyState = pseudoState;
            }

            if (stateData.getPseudoStateKind() == PseudoStateKind.CHOICE) {
                S s = stateData.getState();
                List<ChoiceData<S, E>> list = stateMachineTransitions.getChoices().get(s);
                if (list == null) {
                    throw new MalformedConfigurationException("No transitions for state " + s);
                }
                List<ChoiceStateData<S, E>> choices = new ArrayList<ChoiceStateData<S, E>>();
                for (ChoiceData<S, E> c : list) {
                    StateHolder<S, E> holder = new StateHolder<S, E>(stateMap.get(c.getTarget()));
                    if (holder.getState() == null) {
                        holderList.add(new HolderListItem<S, E>(c.getTarget(), holder));
                    }
                    choices.add(new ChoiceStateData<S, E>(holder, c.getGuard(), Actions.from(c.getActions())));
                }
                PseudoState<S, E> pseudoState = new ChoicePseudoState<S, E>(choices);
                state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(),
                        stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel);
                states.add(state);
                stateMap.put(stateData.getState(), state);
            } else if (stateData.getPseudoStateKind() == PseudoStateKind.JUNCTION) {
                S s = stateData.getState();
                List<JunctionData<S, E>> list = stateMachineTransitions.getJunctions().get(s);
                List<JunctionStateData<S, E>> junctions = new ArrayList<JunctionStateData<S, E>>();
                if (list == null) {
                    throw new MalformedConfigurationException("No transitions for state " + s);
                }
                for (JunctionData<S, E> c : list) {
                    StateHolder<S, E> holder = new StateHolder<S, E>(stateMap.get(c.getTarget()));
                    if (holder.getState() == null) {
                        holderList.add(new HolderListItem<S, E>(c.getTarget(), holder));
                    }
                    junctions.add(new JunctionStateData<S, E>(holder, c.getGuard(), Actions.from(c.getActions())));
                }
                PseudoState<S, E> pseudoState = new JunctionPseudoState<S, E>(junctions);
                state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(),
                        stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel);
                states.add(state);
                stateMap.put(stateData.getState(), state);
            } else if (stateData.getPseudoStateKind() == PseudoStateKind.ENTRY) {
                S s = stateData.getState();
                Collection<EntryData<S, E>> entrys = stateMachineTransitions.getEntrys();
                if (entrys == null) {
                    throw new MalformedConfigurationException("No transitions for state " + s);
                }
                for (EntryData<S, E> entry : entrys) {
                    if (s.equals(entry.getSource())) {
                        PseudoState<S, E> pseudoState = new EntryPseudoState<S, E>(stateMap.get(entry.getTarget()));
                        state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(),
                                stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel);
                        states.add(state);
                        stateMap.put(stateData.getState(), state);
                        break;
                    }
                }
            } else if (stateData.getPseudoStateKind() == PseudoStateKind.EXIT) {
                S s = stateData.getState();
                Collection<ExitData<S, E>> exits = stateMachineTransitions.getExits();
                if (exits == null) {
                    throw new MalformedConfigurationException("No transitions for state " + s);
                }
                for (ExitData<S, E> entry : exits) {
                    if (s.equals(entry.getSource())) {
                        StateHolder<S, E> holder = new StateHolder<S, E>(stateMap.get(entry.getTarget()));
                        if (holder.getState() == null) {
                            holderList.add(new HolderListItem<S, E>(entry.getTarget(), holder));
                        }
                        PseudoState<S, E> pseudoState = new ExitPseudoState<S, E>(holder);
                        state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(),
                                stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel);
                        states.add(state);
                        stateMap.put(stateData.getState(), state);
                        break;
                    }
                }
            } else if (stateData.getPseudoStateKind() == PseudoStateKind.FORK) {
                S s = stateData.getState();
                List<S> list = stateMachineTransitions.getForks().get(s);
                List<State<S, E>> forks = new ArrayList<State<S,E>>();
                if (list == null) {
                    throw new MalformedConfigurationException("No transitions for state " + s);
                }
                for (S fs : list) {
                    forks.add(stateMap.get(fs));
                }
                PseudoState<S, E> pseudoState = new ForkPseudoState<S, E>(forks);
                state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(),
                        stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel);
                states.add(state);
                stateMap.put(stateData.getState(), state);
            } else if (stateData.getPseudoStateKind() == PseudoStateKind.JOIN) {
                S s = stateData.getState();
                List<S> list = stateMachineTransitions.getJoins().get(s);
                if (list == null) {
                    throw new MalformedConfigurationException("No transitions for state " + s);
                }
                List<List<State<S, E>>> joins = new ArrayList<List<State<S, E>>>();

                // if join source is an orthogonal state, assume we're joining by region end states
                // and actually use list of states from each region to support case where one region
                // defines multiple end states.
                if (list.size() == 1) {
                    State<S, E> ss1 = stateMap.get(list.get(0));
                    if (ss1 instanceof RegionState) {
                        Collection<Region<S, E>> regions = ((RegionState<S, E>)ss1).getRegions();
                        for (Region<S, E> r : regions) {
                            List<State<S, E>> j = new ArrayList<State<S, E>>();
                            Collection<State<S, E>> ss2 = r.getStates();
                            for (State<S, E> ss3 : ss2) {
                                if (ss3.getPseudoState() != null && ss3.getPseudoState().getKind() == PseudoStateKind.END) {
                                    j.add(ss3);
                                }
                            }
                            joins.add(j);
                        }
//******************** 下面这个else if是我加的代码,判断如果当前状态是子状态机状态类型,也把子状态的终结状态加入到joins中 ***********************
                    }else if (ss1 instanceof StateMachineState) {
                        Collection<State<S, E>> subStates = ((StateMachineState) ss1).getSubmachine().getStates();
                        List<State<S, E>> k = new ArrayList<State<S, E>>();
                        for (State<S, E> subState : subStates) {
                            if (subState.getPseudoState() != null && subState.getPseudoState().getKind() == PseudoStateKind.END) {
                                k.add(subState);
                            }
                        }
                        joins.add(k);
                    }
                } else {
                    for (S fs : list) {
                        joins.add(Collections.singletonList(stateMap.get(fs)));
                    }
                }

                List<JoinStateData<S, E>> joinTargets = new ArrayList<JoinStateData<S, E>>();
                Collection<TransitionData<S, E>> transitions = stateMachineTransitions.getTransitions();
                for (TransitionData<S, E> tt : transitions) {
                    if (tt.getSource() == s) {
                        StateHolder<S, E> holder = new StateHolder<S, E>(stateMap.get(tt.getTarget()));
                        if (holder.getState() == null) {
                            holderList.add(new HolderListItem<S, E>(tt.getTarget(), holder));
                        }
                        joinTargets.add(new JoinStateData<S, E>(holder, tt.getGuard()));
                    }
                }
                JoinPseudoState<S, E> pseudoState = new JoinPseudoState<S, E>(joins, joinTargets);

                state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(),
                        stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel);
                states.add(state);
                stateMap.put(stateData.getState(), state);
            }
        }

        Collection<Transition<S, E>> transitions = new ArrayList<Transition<S, E>>();
        for (TransitionData<S, E> transitionData : transitionsData) {
            S source = transitionData.getSource();
            S target = transitionData.getTarget();
            E event = transitionData.getEvent();
            Long period = transitionData.getPeriod();
            Integer count = transitionData.getCount();

            Trigger<S, E> trigger = null;
            if (event != null) {
                trigger = new EventTrigger<S, E>(event);
            } else if (period != null) {
                TimerTrigger<S, E> t = new TimerTrigger<S, E>(period, count != null ? count : 0);
                if (beanFactory != null) {
                    t.setBeanFactory(beanFactory);
                }
                trigger = t;
                ((AbstractState<S, E>)stateMap.get(source)).getTriggers().add(trigger);
            }

            if (transitionData.getKind() == TransitionKind.EXTERNAL) {
                // TODO can we do this?
                if (stateMap.get(source) == null || stateMap.get(target) == null) {
                    continue;
                }
                DefaultExternalTransition<S, E> transition = new DefaultExternalTransition<S, E>(stateMap.get(source),
                        stateMap.get(target), transitionData.getActions(), event, transitionData.getGuard(), trigger,
                        transitionData.getSecurityRule(), transitionData.getName());
                transitions.add(transition);

            } else if (transitionData.getKind() == TransitionKind.LOCAL) {
                // TODO can we do this?
                if (stateMap.get(source) == null || stateMap.get(target) == null) {
                    continue;
                }
                DefaultLocalTransition<S, E> transition = new DefaultLocalTransition<S, E>(stateMap.get(source),
                        stateMap.get(target), transitionData.getActions(), event, transitionData.getGuard(), trigger,
                        transitionData.getSecurityRule(), transitionData.getName());
                transitions.add(transition);
            } else if (transitionData.getKind() == TransitionKind.INTERNAL) {
                DefaultInternalTransition<S, E> transition = new DefaultInternalTransition<S, E>(stateMap.get(source),
                        transitionData.getActions(), event, transitionData.getGuard(), trigger,
                        transitionData.getSecurityRule(), transitionData.getName());
                transitions.add(transition);
            }
        }

        if (stateMachineTransitions.getJoins() != null) {
            for (Entry<S, List<S>> entry : stateMachineTransitions.getJoins().entrySet()) {
                if (stateMap.get(entry.getKey()) != null) {
                    List<S> entryList = entry.getValue();
                    for (S entryState : entryList) {
                        State<S, E> source = stateMap.get(entryState);
                        if (source != null && !source.isOrthogonal()) {
                            State<S, E> target = stateMap.get(entry.getKey());
                            DefaultExternalTransition<S, E> transition = new DefaultExternalTransition<S, E>(
                                    source, target, null, null, null, null, null);
                            transitions.add(transition);
                        }
                    }
                }
            }
        }

        Transition<S, E> initialTransition = new InitialTransition<S, E>(initialState, Actions.from(initialAction));
        StateMachine<S, E> machine = buildStateMachineInternal(states, transitions, initialState, initialTransition,
                null, defaultExtendedState, historyState, contextEvents, beanFactory,
                beanName, machineId != null ? machineId : stateMachineModel.getConfigurationData().getMachineId(), uuid, stateMachineModel);
        return machine;
    }

    protected abstract StateMachine<S, E> buildStateMachineInternal(Collection<State<S, E>> states,
                                                                    Collection<Transition<S, E>> transitions, State<S, E> initialState, Transition<S, E> initialTransition,
                                                                    Message<E> initialEvent, ExtendedState extendedState, PseudoState<S, E> historyState,
                                                                    Boolean contextEventsEnabled, BeanFactory beanFactory, String beanName, String machineId, UUID uuid,
                                                                    StateMachineModel<S, E> stateMachineModel);

    protected abstract State<S, E> buildStateInternal(S id, Collection<E> deferred,
                                                      Collection<Function<StateContext<S, E>, Mono<Void>>> entryActions,
                                                      Collection<Function<StateContext<S, E>, Mono<Void>>> exitActions,
                                                      Collection<Function<StateContext<S, E>, Mono<Void>>> stateActions, PseudoState<S, E> pseudoState,
                                                      StateMachineModel<S, E> stateMachineModel);

    private Iterator<Node<StateData<S, E>>> buildStateDataIterator(StateMachineModel<S, E> stateMachineModel) {
        Tree<StateData<S, E>> tree = new Tree<StateData<S, E>>();
        treeAdd(tree, stateMachineModel.getStatesData().getStateData());
        return new TreeTraverser<Node<StateData<S, E>>>() {
            @Override
            public Iterable<Node<StateData<S, E>>> children(Node<StateData<S, E>> root) {
                return root.getChildren();
            }
        }.postOrderTraversal(tree.getRoot()).iterator();
    }

    private void treeAdd(Tree<StateData<S, E>> tree, Collection<StateData<S, E>> stateDatas) {
        // recursive call due to possible submachine data ref
        if (stateDatas == null) {
            return;
        }
        for (StateData<S, E> stateData : stateDatas) {
            tree.add(stateData, stateData.getState(), stateData.getParent());
            treeAdd(tree, stateData.getSubmachineStateData());
        }
    }

    protected abstract RegionState<S, E> buildRegionStateInternal(S id, Collection<Region<S, E>> regions,
                                                                  Collection<E> deferred, Collection<Function<StateContext<S, E>, Mono<Void>>> entryActions,
                                                                  Collection<Function<StateContext<S, E>, Mono<Void>>> exitActions, PseudoState<S, E> pseudoState,
                                                                  StateMachineModel<S, E> stateMachineModel);

    /**
     * Simple utility listener waiting machine to get started if
     * autostart was requestes. Needed for machine to be ready
     * if async executor is used.
     */
    private static class AutostartListener<S, E> extends StateMachineListenerAdapter<S, E> {
        final CountDownLatch latch = new CountDownLatch(1);

        @Override
        public void stateMachineStarted(StateMachine<S, E> stateMachine) {
            latch.countDown();
        }
    }

    private static class HolderListItem<S, E> {
        S key;
        StateHolder<S, E> value;

        public HolderListItem(S key, StateHolder<S, E> value) {
            this.key = key;
            this.value = value;
        }
    }
}
